//
//  CZSQLiteManager.swift
//  数据库
//
//  Created by Summer on 2017/12/25.
//  Copyright © 2017年 Summer. All rights reserved.
//

import Foundation
import FMDB
/*
 FMDB - 使用队列创建
 创建表
 增删改查
 
 
 INSERT INTO T_status (statusId, userId, status) VALUES (100, 1, 'HAHA')
 当主键一样时，使用 INSERT INTO 插入会报错
 INSERT OR REPLACE INTO T_status (statusId, userId, status) VALUES (100, 1, 'HAHA')
 当主键一样时，使用 INSERT OR REPLACE INTO 插入会报错
 
 注意：数据库开发，程序代码几乎是一致的，区别在SQL
      开发数据库功能时，首先一定要在 navicat 中测试 SQL 的正确性
 
 */
class CZSQLiteManager {
    
    static let shared = CZSQLiteManager()
    
    let queue: FMDatabaseQueue
    
    private init () {
        
        let db = "status.db"
        var path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
        path = (path as NSString).appendingPathComponent(db)
        
        // 创建或者打开数据库
        queue = FMDatabaseQueue(path: path)
        
        createTable()
    }
}

// MARK: - 数据库操作
extension CZSQLiteManager {
    
    
    /// 从数据库加载微博数据数组
    ///
    /// - Parameters:
    ///   - userId: 当前登录的userId
    ///   - since_id: 返回比since_id大的微博
    ///   - max_id: 返回比max_id小于的微博
    /// - Returns: 微博的字典的数组，将数据库中status对应的二进制数据，反序列化，生成字典
    func loadStatus(userId: String, since_id: Int64 = 0, max_id: Int64 = 0) -> [[String: Any]] {
        
        var sql = "SELECT statusId, userId, status FROM T_status \n"
        sql += "WHERE userId = \(userId) \n"
        
        //  -- 上拉下拉，针对同一个statusId判断的
        if since_id > 0 {
            sql += "AND statusId > \(since_id) \n"
        } else if max_id > 0 {
            sql += "AND statusId < \(max_id) \n"
        }
        
        sql += "ORDER BY statusId DESC LIMIT 20;"
        // 拼接结束后一定在navicat中测试拼接
        
        
        // 执行sql
        let array = execRecordSet(sql: sql)
        var result = [[String: Any]]()
        
        for dict in array {
            
            guard let jsonData = dict["status"] as? Data,
            let json = try? JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any] else {
                continue
            }
            
            result.append(json ?? [:])
        }
        
        return result
    }
    
    
    /// 新增或者修改微博数据，微博数据在刷新的时候，可能会出现重叠
    ///
    /// - Parameters:
    ///   - userId: 当前登录用户ID
    ///   - array: 从网络获取的字典数组
    func updateStatus(userId: String, array: [[String: Any]]) {
        
        let sql = "INSERT OR REPLACE INTO T_status (statusId, userId, status) VALUES (?, ?, ?);"
        
        // 多条数据，使用开启事务方法 inTransaction
        // rollback 回滚
        queue.inTransaction { (db, rollback) in
            
            // 遍历数组，逐条微博插入数据库
            for dict in array {
                
                // 从字典获取微博代号，将微博序列化成二进制数据
                guard let statusId = dict["idstr"] as? String,
                    let jsonData = try? JSONSerialization.data(withJSONObject: dict, options: []) else {
                    continue
                }
                
                // 执行sql
                if db.executeUpdate(sql, withArgumentsIn: [statusId, userId, jsonData]) == false {
                    
                    // 插入失败，需要回滚，
                    // OC中     *rollback = YES
                    // Swift中 rollback.pointee = true
                    rollback.pointee = true
                    break
                }
                
                // MARK: 模拟回滚
                // rollback.pointee = true
                // break
            }
            
        }
    }
    
}

// MARK: - 创建数据库表操作以及其他私有方法
extension CZSQLiteManager {
    
    /// 执行一个SQL，返回一个字典数组
    ///
    /// - Parameter sql: sql
    /// - Returns: 字典的数组
    func execRecordSet(sql: String) -> [[String: Any]] {
        
        var result = [[String: Any]]()
        
        // 执行sql，查询数据，不会修改数据，所以不需要开启事务 inTransaction
        // 事务的目的：为了保证数据的有效性，一旦失败，回滚到初始状态
        queue.inDatabase { (db) in
            
            guard let rs =  db.executeQuery(sql, withArgumentsIn: []) else {
                return
            }
            
            // 逐行，遍历结果集合
            while rs.next() {
                
                // 1> 列数
                let colCount = rs.columnCount
                
                // 2> 遍历所有列
                for col in 0..<colCount {
                    
                    // 3> 列名 -> KEY
                    // 4> 值 -> VALUE
                    guard let name = rs.columnName(for: col),
                        let value = rs.object(forColumnIndex: col) else {
                        continue
                    }
                    
//                    print("\(name) --- \(value)")
                    result.append([name: value])
                }
            }
        }
        return result
    }
    
    
    func createTable() {
        
        // 准备sql
        guard let path = Bundle.main.path(forResource: "status.sql", ofType: nil),
            let sql = try? String(contentsOfFile: path) else {
            return
        }
        
        // 执行sql
        // FMDB 内部是串行队列，同步执行
        // 保证同一时间，只有一个任务操作数据库，从而保证数据库的读写安全
        queue.inDatabase { (db) in
            
            // 只有在创表的时候，使用执行多条语句方法，可以一次创建多个表
            // 在执行增删改查的时候，一定不要执行 statements 方法，有可能会 sql 注入
            if db.executeStatements(sql) {
                print("创建或打开成功")
            } else {
                print("创建失败")
            }
        }
        print("over")
        // 执行顺序，创建成功 - over
    }
}
